﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SceneManager.Components;

namespace SceneManager
{
	public class GameObjectCamera : ICamera
	{
		#region Variables

		private Viewport _viewport;
		private GameObject _gameObject;
		private OccupiesSpaceComponent _occupiesSpace;
		private Vector2 _offset;
		private float _scale;

		#endregion

		#region Constructors

		public GameObjectCamera(Viewport viewport, GameObject gameObject)
		{
			_occupiesSpace = gameObject.Components.Find<OccupiesSpaceComponent>();
			if (_occupiesSpace == null)
			{
				throw new InvalidOperationException("The game object does not have an OccupiesSpaceComponent defined.");
			}

			_gameObject = gameObject;
			_viewport = viewport;
			_offset = new Vector2(_viewport.Width, _viewport.Height) / 2.0f;

			MinZoom = 0.5f;
			MaxZoom = 2.0f;
			_scale = 1.0f;
		}

		#endregion

		#region Properties

		public float MinZoom { get; set; }

		public float MaxZoom { get; set; }

		public float Zoom
		{
			get
			{
				return _scale;
			}
			set
			{
				_scale = MathHelper.Clamp(value, MinZoom, MaxZoom);
			}
		}

		/// <summary>
		/// How far is the camera allowed to go in following the game object?
		/// </summary>
		public Rectangle Bounds { get; set; }

		public GameObject Focus
		{
			get
			{
				return _gameObject;
			}
			set
			{
				_occupiesSpace = value.Components.Find<OccupiesSpaceComponent>();
				if (_occupiesSpace == null)
				{
					throw new InvalidOperationException("The game object does not have an OccupiesSpaceComponent defined.");
				}
			}
		}

		#endregion

		#region Methods

		public Matrix GetTransformMatrix()
		{
			if (_occupiesSpace == null)
			{
				return Matrix.Identity;
			}

			var position = _occupiesSpace.Position;
			if ((Bounds.Width != 0) && (Bounds.Height != 0))
			{
				position.X = MathHelper.Clamp(position.X, Bounds.Left + (_viewport.Width / Zoom) / 2, Bounds.Right - (_viewport.Width / Zoom) / 2);
				position.Y = MathHelper.Clamp(position.Y, Bounds.Top + (_viewport.Height / Zoom) / 2, Bounds.Bottom - (_viewport.Height / Zoom) / 2);
			}

			var translation = _offset - position * Zoom;
			translation.X = (int)translation.X;
			translation.Y = (int)translation.Y;

			return
				Matrix.CreateScale(Zoom) *
				Matrix.CreateTranslation(new Vector3(translation, 0.0f));
		}

		#endregion
	}
}